﻿using System.Collections.Generic;
using System.Linq;
using Kristall;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Windows.Forms;
using System.IO;
using System;
using Kristall.Engine;
using Kristall.Editor.Commands;
using Kristall.Managers;
using Kristall.Editor;

namespace Kristall.Screens
{
    using Keys = Microsoft.Xna.Framework.Input.Keys;
    using ButtonState = Microsoft.Xna.Framework.Input.ButtonState;

    public class LevelEditor : GameScreen
    {
        public Level Level;
        public static Camera2D Camera;

        private List<Tool> tools = new List<Tool>();
        public Tool CurrentTool;

        public ContentManager Content;

        public ToolPalette ToolPalette;

        public RectangleTool RectangleTool;
        public SelectTool SelectTool;
        //public MoveTool MoveTool;

        private MouseState currentMouseState = Mouse.GetState();
        private MouseState lastMouseState;
        private Vector2 mouseStartDrag;

        public bool UpdateLevel = false;

        Control xnaWindow;

        public Vector2 MouseCoordinates
        {
            get
            {
                return Camera.ScreenToWorld(new Vector2(currentMouseState.X, currentMouseState.Y));
            }
        }

        string _currentCfg;

        private void reset()
        {
            Level.LoadContent();
            tools.ForEach(x => x.Reset());
            ToolPalette.CommandManager.ClearHistory();

            Camera = new Camera2D()
            {
                Spring = false
            };
            Camera.SetPosition(Level.Player.Center);

            _currentCfg = Kristall.Options.cfg_name;
            Kristall.Options = Options.Load("editor.xml");

            SelectTool.SelectionManager.ClearSelection();
        }

        public LevelEditor(Game game)
        {
            xnaWindow = Control.FromHandle(game.Window.Handle);
            ToolPalette = new ToolPalette(this, xnaWindow);
        }

        public void NewLevel()
        {
            Level = new Level();
            reset();
        }

        public void LoadLevel(string levelName)
        {
            Level = Level.Load(levelName);
            reset();
        }

        public override void LoadContent()
        {
            if (Content == null)
            { 
                Content = new ContentManager(ScreenManager.Game.Services);
                Content.RootDirectory = "Content";
            }

            //gameWindow = Control.FromHandle(this.ScreenManager.Game.Window.Handle);

            RectangleTool = new RectangleTool(this);
            SelectTool = new SelectTool(this);
            //MoveTool = new MoveTool(this);

            tools.Add(RectangleTool);
            tools.Add(SelectTool);
            //tools.Add(MoveTool);

            foreach (Tool t in tools)
            {
                t.LoadContent(Content);
            }

            CurrentTool = tools.First();

            if (File.Exists(Level.FullLevelsPath + Kristall.DS + "_temp.lvl"))
            {
                LoadLevel(Level.FullLevelsPath + Kristall.DS + "_temp.lvl");
            }
            else
            {
                NewLevel();
            }
        }

        string tempFileName = Level.FullLevelsPath + Kristall.DS + "_temp.lvl";

        public override void HandleInput(InputState input, GameTime gameTime)
        {
            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (!this.IsActive)
            {
                return;
            }

            //Show mouse pointer if the window doesnt have focus
            ScreenManager.Game.IsMouseVisible = !xnaWindow.Focused;

            if (!xnaWindow.Focused)
            {
                return;
            }

            foreach (Tool t in tools)
            {
                if (input.IsKeyPressed(t.Hotkey))
                {
                    CurrentTool = t;
                    break;
                }
            }

            CurrentTool.HandleInput(input);

            lastMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();

            int scroll = currentMouseState.ScrollWheelValue - lastMouseState.ScrollWheelValue;
            if (scroll != 0)
            {
                Kristall.Options.camera_scale += scroll * 0.001f;
            }

            handleCameraInput(input, dt);

            nudgeSelection(input);

            if (input.IsKeyPressed(Keys.Escape))
            {
                if (MessageBox.Show("Are you sure you want to quit the Level Editor?", "Quit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    ExitToMenu();
                    return;
                }
            }

            if (input.IsKeyPressed(Keys.P))
            {
                RunLevel();
                return;
            }

            if (input.IsKeyPressed(Keys.F))
            {
                AABB aabb;
                if((aabb = SelectTool.SelectionManager.SelectedObject as AABB) != null)
                {
                    Camera.SetPosition(aabb.Center);
                }
            }

            Keys[] lastKeys = input.LastKeyboardStates.GetPressedKeys();
            Keys[] currentKeys = input.CurrentKeyboardStates.GetPressedKeys();

            List<Keys> newPressed = currentKeys.Where(x => !lastKeys.Contains(x)).ToList();

            if (newPressed.Any())
            {
                System.Windows.Forms.Keys keys = System.Windows.Forms.Keys.None;

                if (currentKeys.Contains(Keys.LeftControl) || currentKeys.Contains(Keys.RightControl))
                {
                    keys |= System.Windows.Forms.Keys.Control;
                    newPressed.Remove(Keys.LeftControl);
                    newPressed.Remove(Keys.RightControl);
                }

                if (currentKeys.Contains(Keys.LeftAlt) || currentKeys.Contains(Keys.RightAlt))
                {
                    keys |= System.Windows.Forms.Keys.Alt;
                    newPressed.Remove(Keys.LeftAlt);
                    newPressed.Remove(Keys.RightAlt);
                }

                if (currentKeys.Contains(Keys.LeftShift) || currentKeys.Contains(Keys.RightShift))
                {
                    keys |= System.Windows.Forms.Keys.Shift;
                    newPressed.Remove(Keys.LeftShift);
                    newPressed.Remove(Keys.RightShift);
                }

                newPressed.ForEach(x => keys |= (System.Windows.Forms.Keys)x);

                KeyEventArgs e = new KeyEventArgs(keys);
                ToolPalette.TriggerShortcutKey(e);
            }
        }

        public void ExitToMenu()
        {
            ScreenManager.AddScreen(new BackgroundScreen());
            ScreenManager.AddScreen(new MainMenuScreen());
            ExitScreen();
        }

        private void nudgeSelection(InputState input)
        {
            AABB temp = SelectTool.SelectionManager.SelectedObject;


            if (temp != null && !input.CurrentKeyboardStates.IsKeyDown(Keys.LeftShift))
            {
                float distance = input.CurrentKeyboardStates.IsKeyDown(Keys.LeftControl) ? Kristall.Options.nudge_distance : 1;
                Vector2 newPosition = Vector2.Zero;

                if (input.IsKeyPressed(Keys.Left))
                {
                    newPosition -= Vector2.UnitX * distance;
                }

                if (input.IsKeyPressed(Keys.Right))
                {
                    newPosition += Vector2.UnitX * distance;
                }

                if (input.IsKeyPressed(Keys.Up))
                {
                    newPosition -= Vector2.UnitY * distance;
                }

                if (input.IsKeyPressed(Keys.Down))
                {
                    newPosition += Vector2.UnitY * distance;
                }

                if (!newPosition.Equals(Vector2.Zero))
                {
                    MoveCommand moveCommand = new MoveCommand(temp, temp.Position + newPosition);
                    ToolPalette.CommandManager.ExecuteCommand(moveCommand);
                }
            }
        }

        public void RunLevel()
        {
            Kristall.Options.Save();
            Kristall.Options = Options.Load(_currentCfg);

            Level.Save(tempFileName);

            ScreenManager.AddScreen(new LoadingScreen(new GameplayScreen(tempFileName, true)));
        }

        public override void UnloadContent()
        {
            base.UnloadContent();

            Content.Unload();

            Level.UnloadContent();

            ToolPalette.Close();
        }

        private void handleCameraInput(InputState input, float dt)
        {
            if (lastMouseState.MiddleButton == ButtonState.Released && currentMouseState.MiddleButton == ButtonState.Pressed)
            {
                mouseStartDrag = new Vector2(currentMouseState.X, currentMouseState.Y);
            }

            if (currentMouseState.MiddleButton == ButtonState.Pressed)
            {
                //Vector2 drag = Camera.GetPosition() + (new Vector2(currentMouseState.X, currentMouseState.Y) - mouseStartDrag);
                //Camera.Update(drag, dt);
            }

            ScrollCamera(input);
        }

        public void ScrollCamera(InputState input)
        {
            Vector2 scroll = Vector2.Zero;
            float scrollSpeed = 10f;

            if (input.CurrentKeyboardStates.IsKeyDown(Keys.LeftControl))
                scrollSpeed *= 5;

            Vector2 mouseScreen = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

            if (input.CurrentKeyboardStates.IsKeyDown(Keys.LeftShift))
            {
                if (mouseScreen.X < 0)
                    scroll -= Vector2.UnitX;
                else if (Kristall.Viewport.X < mouseScreen.X)
                    scroll += Vector2.UnitX;
                if (mouseScreen.Y < 0)
                    scroll -= Vector2.UnitY;
                else if (Kristall.Viewport.Y < mouseScreen.Y)
                    scroll += Vector2.UnitY;
            }
            else if (Mouse.GetState().MiddleButton == ButtonState.Pressed)
            {
                scroll = (mouseScreen - mouseStartDrag) / (Vector2.One * 300f);
                if(scroll.Length() > 1f)
                    scroll.Normalize();
            }
            else if (SelectTool.SelectionManager.SelectedObject == null)
            {
                if (input.CurrentKeyboardStates.IsKeyDown(Keys.Left))
                    scroll -= Vector2.UnitX;
                if (input.CurrentKeyboardStates.IsKeyDown(Keys.Right))
                    scroll += Vector2.UnitX;
                if (input.CurrentKeyboardStates.IsKeyDown(Keys.Up))
                    scroll -= Vector2.UnitY;
                if (input.CurrentKeyboardStates.IsKeyDown(Keys.Down))
                    scroll += Vector2.UnitY;
            }

            if (!scroll.Equals(Vector2.Zero))
                Camera.SetPosition(LevelEditor.Camera.GetPosition() + scroll * scrollSpeed);
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            SpriteBatch batch = ScreenManager.SpriteBatch;

            Level.Draw(batch, Camera);

            batch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null, null, Camera.World);

            if (IsActive)
            { 
                CurrentTool.Draw(batch);
            }

            batch.End();
        }

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            if (!IsActive)
                return;

            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (UpdateLevel)
            { 
                Level.Update(dt);
            }

            CurrentTool.Update(dt);
        }
    }
}
